#include "CUpLoadServer.h"
#include "CTableManager.h"
#include "CTableBase.h"
#include "CUploadBatch.h"
#include "CTableConfigure.h"
#include <iostream>
#include <sstream>
#include <chrono>
#include <thread>

CUpLoadServer::CUpLoadServer()
	: m_tableManager(nullptr),
	  m_maxRetryCount(10),
	  m_retryIntervalSeconds(5),
	  m_running(false)
{
}

CUpLoadServer::~CUpLoadServer()
{
	stop();
}

void CUpLoadServer::setTableManager(CTableManager* tableManager)
{
	m_tableManager = tableManager;
}

int CUpLoadServer::start()
{
	if (m_running) {
		return 0; // Already running
	}
	
	if (!m_tableManager) {
		std::cout << "Error: Table manager not set" << std::endl;
		return -1;
	}
	
	if (!m_uploadCallback) {
		std::cout << "Error: Upload callback not set" << std::endl;
		return -2;
	}
	
	m_running = true;
	m_uploadThread = boost::thread(&CUpLoadServer::uploadLoop, this);

	
	std::cout << "Upload server started" << std::endl;
	return 0;
}

int CUpLoadServer::stop()
{
	if (!m_running) {
		return 0; // Already stopped
	}
	
	m_running = false;
	
	if (m_uploadThread.joinable()) {
		m_uploadThread.join();
	}

	
	std::cout << "Upload server stopped" << std::endl;
	return 0;
}

void CUpLoadServer::setRetryConfig(int maxRetryCount, int retryIntervalSeconds, int timeOutIntervalSeconds)
{
	m_maxRetryCount = maxRetryCount;
	m_retryIntervalSeconds = retryIntervalSeconds;
	m_timeOutIntervalSeconds = timeOutIntervalSeconds;
}



void CUpLoadServer::setUploadCallback(const UploadCallback& uploadCallback)
{
	m_uploadCallback = uploadCallback;
}


void CUpLoadServer::onUploadResult(const std::string& resultJson)
{
	try {
		// Parse the result JSON
		json result = json::parse(resultJson);
		
		// Extract batchId from the result
		std::string batchId;
		if (result.contains("batchId")) {
			batchId = result["batchId"];
		} else {
			std::cout << "No batchId found in upload result" << std::endl;
			return;
		}
		
		// Check if this is the current upload batch
		if (m_currentUploadBatch && batchId == m_currentUploadBatch->getBatchId()) {
			handleUploadSuccess(m_currentUploadBatch);
		} else {
			std::cout << "Batch ID mismatch or no current upload batch" << std::endl;
		}
	} catch (const std::exception& e) {
		std::cout << "Error parsing upload result: " << e.what() << std::endl;
	}
}

void CUpLoadServer::uploadLoop()
{

	try
	{
		while (m_running)
		{
			if (m_currentUploadBatch)
			{
				if (m_currentUploadBatch->isUploading())
				{
					if (m_currentUploadBatch->isTimedOut())
					{
						handleUploadFailed(m_currentUploadBatch);
						std::this_thread::sleep_for(std::chrono::seconds(m_retryIntervalSeconds));
						handleUpload(m_currentUploadBatch);
					}
				}
			}
			else
			{
				CUpLoadBatchPtr batchPtr = getUpLoadBatch();
				m_currentUploadBatch = batchPtr;
				handleUpload(batchPtr);
			}

			std::this_thread::sleep_for(std::chrono::milliseconds(100));
		}
	}
	catch (const std::exception&){}
	catch (...) {}
}


std::string CUpLoadServer::serializeUploadBatch(const CUpLoadBatchPtr& uploadBatch)
{
	json j;
	
	CTableBasePtr tablePtr = m_tableManager->GetTable(uploadBatch->GetTableName());

	int tableType = tablePtr->getTableType();
	std::string tableTypeStr;
	switch (tableType) {
	case 1: tableTypeStr = "append"; break;
	case 2: tableTypeStr = "upappend"; break;
	case 3: tableTypeStr = "update"; break;
	case 4: tableTypeStr = "appendwithcounter"; break;
	case 5: tableTypeStr = "over"; break;
	default: tableTypeStr = "unknown"; break;
	}

	// Add batch ID from the upload batch
	j["batchId"] = uploadBatch->getBatchId();
	
	// Create logs array
	json logsArray = json::array();
	json logEntry;
	
	// Set table name and type
	logEntry["table"] = uploadBatch->GetTableName();
	logEntry["table_type"] = tableType;
	
	// Create records array
	json recordsArray = json::array();
	
	// Get records from upload batch
	const auto& records = uploadBatch->GetRecords();
	
	for (const auto& record : records) {
		json recordJson;
		
		// Add all fields from the record
		const auto& fields = record->GetFields();
		for (const auto& fieldPair : fields) {
			const std::string& fieldName = fieldPair.first;
			const auto& field = fieldPair.second;
			const auto& value = field->GetValue();
			
			// Convert value to JSON based on type
			if (value.type() == typeid(uint64_t)) {
				recordJson[fieldName] = boost::get<uint64_t>(value);
			} else if (value.type() == typeid(double)) {
				recordJson[fieldName] = boost::get<double>(value);
			} else if (value.type() == typeid(std::string)) {
				recordJson[fieldName] = boost::get<std::string>(value);
			}
		}
		
		recordsArray.push_back(recordJson);
	}
	
	logEntry["records"] = recordsArray;
	logsArray.push_back(logEntry);
	j["logs"] = logsArray;
	
	return j.dump();
}

int CUpLoadServer::handleUploadSuccess(const CUpLoadBatchPtr& uploadBatch)
{
	CTableBasePtr table = m_tableManager->GetTable(uploadBatch->GetTableName());
	std::string tableName = table->getTableName();
	
	int result = table->batchUploadSuccess(uploadBatch);
	if (result != 0) {
		std::cout << "Failed to update cursor for table: " << tableName << std::endl;
		return result;
	}
	
	table->setUploadFailedCount(0);
	
	m_currentUploadBatch = nullptr;

	return 0;
}

int CUpLoadServer::handleUploadFailed(const CUpLoadBatchPtr& uploadBatch)
{
	std::string tableName = uploadBatch->GetTableName();
	CTableBasePtr table = m_tableManager->GetTable(tableName);
	if (!table) {
		return -1;
	}

	int tableFailedCount = table->getUploadFailedCount();
	table->setUploadFailedCount(tableFailedCount + 1);
	
	int batchFailedCount = uploadBatch->getFailedCount();
	if (batchFailedCount > m_maxRetryCount) {
		m_currentUploadBatch = nullptr;
	} else {
		uploadBatch->addFailedCount();
	}

	return 0;
}

int CUpLoadServer::handleUpload(const CUpLoadBatchPtr& uploadBatch)
{
	uploadBatch->setUploading(true);
	uploadBatch->setUploadStartTime();

	std::string jsonData = serializeUploadBatch(uploadBatch);

	if (m_uploadCallback) {
		m_uploadCallback(jsonData);
	}
	return 0;
}

CUpLoadBatchPtr CUpLoadServer::getUpLoadBatch()
{
	if (!m_tableManager) {
		return nullptr;
	}
	
	// Get all tables from the manager
	auto& allTables = m_tableManager->getAllTables();
	if (allTables.empty()) {
		return nullptr;
	}
	
	// Create a vector of table pointers for sorting
	std::vector<CTableBasePtr> tableVector;
	for (const auto& pair : allTables) {
		if (pair.second) {
			tableVector.push_back(pair.second);
		}
	}
	
	// Sort tables by priority (level) - lower level means higher priority
	std::sort(tableVector.begin(), tableVector.end(), 
		[](const CTableBasePtr& a, const CTableBasePtr& b) {
			return a->getLevel() < b->getLevel();
		});
	
	// Try each table in priority order
	for (const auto& table : tableVector) {
		if (!table) {
			continue;
		}
		
		// Check if this table can upload
		if (!table->canUpload()) {
			std::cout << "Table " << table->getTableName() << " cannot upload (failed count: " 
					  << table->getUploadFailedCount() << ")" << std::endl;
			continue;
		}
		
		// Create a new upload batch
		CUpLoadBatchPtr uploadBatch = std::make_shared<CUploadBatch>();
		uploadBatch->SetTimeOutIntervalSeconds(m_timeOutIntervalSeconds);

		// Try to get upload batch from this table
		int result = table->getUploadBatch(uploadBatch);
		if (result != 0) {
			std::cout << "Failed to get upload batch from table: " << table->getTableName() 
					  << " (error: " << result << ")" << std::endl;
			continue;
		}
		
		// Check if we got any records
		if (uploadBatch->GetMaxCursor() == 0) {
			std::cout << "No records to upload from table: " << table->getTableName() << std::endl;
			continue;
		}
		
		// Successfully got upload batch from this table
		std::cout << "Got upload batch from table: " << table->getTableName() 
				  << " (records: " << uploadBatch->GetRecords().size() 
				  << ", max cursor: " << uploadBatch->GetMaxCursor() << ")" << std::endl;
		
		return uploadBatch;
	}
	
	// No table has data to upload
	std::cout << "No tables have data to upload" << std::endl;
	return nullptr;
}
